package com.atguigu.tingshu.album.service.impl;

import com.atguigu.tingshu.album.service.TestService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @author atguigu-mqx
 * @ClassName TestServiceImpl
 * @description: TODO
 * @date 2024年06月15日
 * @version: 1.0
 */
@Service
public class TestServiceImpl implements TestService {

    //  string list hash set Zset;
    @Autowired
    private StringRedisTemplate redisTemplate;

    private static final ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(8);

    //  JUC - 同步方法锁是谁? this 同步代码块锁：(object) 静态方法锁：类的字节码文件TestServiceImpl.class
    @Override
    public synchronized void testLock() {
        //  判断是否有锁 setnx lock 1;
        //  Boolean result = this.redisTemplate.opsForValue().setIfAbsent("lock", "1");
        //  set lock 1 nx ex 10
        String token = UUID.randomUUID().toString();
        Boolean result = this.redisTemplate.opsForValue().setIfAbsent("lock", token, 3, TimeUnit.SECONDS);
        if (result) {
            try {
                //  定时任务需要 或 可以使用守护线程
                scheduledThreadPool.scheduleAtFixedRate(
                        () -> redisTemplate.expire("lock" , 30 ,
                                TimeUnit.SECONDS) ,
                        200 ,
                        200 ,
                        TimeUnit.MILLISECONDS) ;
                //  设置过期时间：10s
                //  this.redisTemplate.expire("lock",10, TimeUnit.SECONDS);
                /*
                    1.  要从缓存中查询一个数据 key = num
                    2.  true: +1,放入缓存 false: return;
                 */
                String num = redisTemplate.opsForValue().get("num");
                //  判断
                if (StringUtils.isBlank(num)) {
                    return;
                }
                //  放入缓存
                this.redisTemplate.opsForValue().set("num", String.valueOf(Integer.parseInt(num) + 1));

                //  防止误删锁！-- 业务执行时间>锁的过期时间 ，可能出现误删锁，同时会出现 多线程共享资源，可能会导致数据不安全！
                //                if (token.equals(this.redisTemplate.opsForValue().get("lock"))){
                //                    //  是否锁：
                //                    this.redisTemplate.delete("lock");
                //                }
                String scriptText = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                        "then\n" +
                        "    return redis.call(\"del\",KEYS[1])\n" +
                        "else\n" +
                        "    return 0\n" +
                        "end";
                DefaultRedisScript<Long> script = new DefaultRedisScript<>();
                script.setScriptText(scriptText);
                //  删除锁--
                this.redisTemplate.execute(script, Arrays.asList("lock"), token);
            } finally {

            }

        } else {
            try {
                Thread.sleep(100);
                //  自旋
                testLock();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}
